/* -----------------
* brief
* 1. this is code gen by tools
* 2. mvc code are driver by the model defined with the same filename,
	 for example:
	 there is a Xmodel struct define in xmodel.go file, after the script run (lego model-mvc module_name xmodel)
     it will auto create the code under the module's path
	 m --> xmodel.go
	 v --> view
	 c --> xmodel_ctrl
* 3. you should modify this code if it doesn't satisfy your purpose
*/

package controller

import (
	"encoding/json"
	"github.com/mjiulee/lego"
	"github.com/mjiulee/lego/utils"
	"github.com/valyala/fasthttp"
	"saas_sys_base/common"
	"saas_sys_base/common/err_const"
	appmanagerService "saas_sys_base/modules/appmanager/service"
	tenantService "saas_sys_base/modules/tenant/service"
	"strings"

	. "saas_sys_base/modules/tenant/models"
	. "saas_sys_base/modules/user/service"
)

var _UserApiCtrl *UserApiCtrl

type UserApiCtrl struct {
	wxUserservice       WxUserService
	webUserservice      WebUserService
	miniAppUserService  MiniAppUserService
	appManagerService   appmanagerService.AppManagerService
	tenantService tenantService.TenantService
}

func GetInstanceUserApiCtrl() *UserApiCtrl {
	return _UserApiCtrl
}

func init() {
	vc := new(UserApiCtrl)
	_UserApiCtrl = vc
	_ctrlList = append(_ctrlList, vc)
}

func (vc *UserApiCtrl) CtrlInfo() string {
	return "UserApiCtrl"
}

func (vc *UserApiCtrl) RoutInit() {
	//业务接口
	// 1. 小程序登陆获取token
	lego.APIPOST("/api/user/login", vc.UserLogin, false)

	// 2. 获取用户信息
	lego.APIPOST("/api/user/profile", vc.UserProfile, false)

	// 3. 微应用登录接口
	lego.APIPOST("/api/user/miniapp_login", vc.MiniAppUserLogin, false)

	lego.APIPOST("/api/user/wx_update_phone", vc.WxUpdatePhone, true)
}

/* 获取token
* params:
  ---
*/
func (vc *UserApiCtrl) UserLogin(ctx *fasthttp.RequestCtx) {
	ctxExt := lego.RequestCtxExtent{ctx}
	response := make(map[string]interface{})
	//data := make(map[string]interface{})

	type Resp struct {
		*LoginUser
		Token string
		AppToken string
		LastTenant *Tenant
	}
	code := 0
	msg := "success"

	for {
		//rawcode := string(ctxExt.Peek("code"))
		//tid, _ := utils.StringToInt64(string(ctxExt.Peek("tid"))) //tenant id
		//appid, _ := utils.StringToInt64(string(ctxExt.Peek("appid"))) //tenant id
		//shareid := string(ctx.FormValue("shareid")) // 谁邀请的
		//rawData := string(ctx.PostArgs().Peek("rawData"))
		//BODY内json数据的定义
		type Request struct {
			Code  string `json:"code" vd:"$!=''"`
			Tid   int64  `json:"tid,omitempty" vd:"$==0"`  //TODO 要不要允许非零的tid?要考虑一下
			AppId int64  `json:"appid,omitempty" vd:"$>0"` //内部app id, internal app id
			//rawData string `json:"rawData,omitempty" vd:""`
			EncryptedData string `json:"encryptedData,omitempty" vd:""`
			Iv string `json:"iv,omitempty" vd:""`
		}

		//url querystring格式定义
		type UrlParam struct {
			Code  string `urlenc:"code"`
			Tid   int64  `urlenc:"tid"`
			AppId int64  `urlenc:"appid"`
		}

		var req Request
		var urlParam UrlParam
		if err := ctxExt.UrlDecode(&urlParam); err != nil {
			code = 100
			msg = err.Error()
			break
		}
		requestByte := ctxExt.PostBody()
		err := json.Unmarshal(requestByte, &req)
		if err != nil {
			code = 100
			msg = err.Error()
			break
		}

		//UrlPara的值优先
		req.Code = urlParam.Code
		if urlParam.Code != "" {
			req.Code = urlParam.Code
		}

		if urlParam.AppId != 0 {
			req.AppId = urlParam.AppId
		}

		if urlParam.Tid != 0 {
			req.Tid = urlParam.Tid
		}

		if err = lego.Validate(req); err != nil {
			code = 100
			msg = err.Error()
			break
		}

		//如果tid==0且app_id==0，返回参数错误
		//fmt.Println(rawData)
		if err != nil {
			code = 100
			msg = err.Error()
			break
		}

		if len(req.Code) <= 0 {
			code = 100
			msg = "参数错误"
			break
		}

		reqwho := common.NewWho(req.Tid, req.AppId, 0)
		hasPermission, err := vc.appManagerService.CheckAppPermission(reqwho)
		if !hasPermission {
			code = 100
			msg = err.Error()
			break
		}

		/*isTenantApp := vc.appManagerService.IsTenantApp(*reqwho)
		if isTenantApp {
			code = 100
			msg = "not support login for tenant user"
			break
		}*/

		hasPermission, err = vc.appManagerService.CheckAppTenantPermission(reqwho)
		if !hasPermission {
			msg = err.Error()
			break
		}

		User, tokenString, err := vc.wxUserservice.UserLogin(*reqwho, req.Code, req.EncryptedData, req.Iv)
		if err != nil {
			code = 100
			msg = err.Error()
			break
		}

		reqwho.Uid = User.User.Id //登录成功后，取到uid，更新到reqwho变量中
		lastTenant, _ := vc.tenantService.GetCurrentTenant(reqwho)

		resp := &Resp{}
		resp.LoginUser = User
		resp.Token = tokenString

		//data["token"] = tokenString
		resp.AppToken = tokenString
		resp.LastTenant = lastTenant
		response["data"] = resp
		break
	}

	response["code"] = code
	response["msg"] = msg
	ctxExt.JSON(200, response)
	return
}

/* 获取token
* params:
  ---
*/
func (vc *UserApiCtrl) UserProfile(ctx *fasthttp.RequestCtx) {
	ctxExt := lego.RequestCtxExtent{ctx}
	response := make(map[string]interface{})
	//data := make(map[string]interface{})

	code := 0
	msg := "success"

	for {
		type HeaderParam struct {
			Token     string
			Timestamp string
			LoginMode int

			AppToken     string
			AppTimestamp string
		}

		var headerparam = &HeaderParam{
			Token:     string(ctxExt.Request.Header.Peek("token")),
			Timestamp: string(ctxExt.Request.Header.Peek("timestamp")),
			LoginMode: utils.BytesToInt(ctxExt.Request.Header.Peek("loginmode")),

			AppToken:     string(ctxExt.Request.Header.Peek("apptoken")),
			AppTimestamp: string(ctxExt.Request.Header.Peek("apptimestamp")),
		}
		_, User, err := vc.wxUserservice.CheckToken(headerparam.Token, headerparam.Timestamp)
		if err != nil {
			code = 100
			msg = err.Error()
			break
		}

		response["data"] = User
		break
	}

	response["code"] = code
	response["msg"] = msg
	ctxExt.JSON(200, response)
	return
}

func (vc *UserApiCtrl) CheckToken(ctxExt *lego.RequestCtxExtent, passToken string, timestamp string, loginmode int) (userwho *common.Who, user *LoginUser, err error) {
	if loginmode == common.LOGIN_MODE_WECHAT {
		userwho, user, err = vc.wxUserservice.CheckToken(passToken, timestamp)
	} else if loginmode == common.LOGIN_MODE_WEB {
		userwho, user, err = vc.webUserservice.CheckToken(passToken, timestamp)
	}

	return userwho, user, err
}

func (vc *UserApiCtrl) MiniAppCheckToken(ctxExt *lego.RequestCtxExtent, passToken string, timestamp string) (userwho *common.Who, err error) {
	userwho, _, err = vc.miniAppUserService.CheckToken(passToken, timestamp)
	return userwho, err
}

func (vc *UserApiCtrl) MiniAppUserLogin(ctx *fasthttp.RequestCtx) {
	ctxExt := lego.RequestCtxExtent{ctx}
	response := make(map[string]interface{})
	//data := make(map[string]interface{})

	//sessionData := common.GetSessionData(&ctxExt)
	type Resp struct {
		AppToken string
	}
	code := 0
	msg := "success"

	for {
		//先检测sys token
		type HeaderParam struct {
			Token     string
			Timestamp string
			LoginMode int
		}

		var headerparam = &HeaderParam{
			Token:     string(ctxExt.Request.Header.Peek("token")),
			Timestamp: string(ctxExt.Request.Header.Peek("timestamp")),
			LoginMode: utils.BytesToInt(ctxExt.Request.Header.Peek("loginmode")),
		}

		//url querystring格式定义
		type UrlParam struct {
			Tid   int64 `urlenc:"tid"`
			AppId int64 `urlenc:"appid"`
		}

		var urlParam UrlParam
		if err := ctxExt.UrlDecode(&urlParam); err != nil {
			code = err_const.ERR_DECODE_FAIL
			msg = err.Error()
			break
		}

		//检查参数取值
		if urlParam.AppId == 0 {
			ctxExt.ResponseWithStateCode200(err_const.ERR_PARA_ERR, "appid为空")
			return
		}

		// TODO 后续考虑还需要check sys token吗？
		userwho, _, err := vc.CheckToken(&ctxExt, headerparam.Token, headerparam.Timestamp, headerparam.LoginMode)
		if err != nil {
			ctxExt.ResponseWithStateCode200(err_const.ERR_TOKEN_INVALID, err.Error())
			return
		}

		uid := userwho.Uid //checkToken返回的Uid //sessionData.TokenWho.Uid
		reqAppWho := common.NewWho(urlParam.Tid, urlParam.AppId, uid)

		//先校验APP有无权限（比如是否上线）
		/*系统APP的校验在中间件层校验， hasPermission, err := vc.appManagerService.CheckAppPermission(*reqwho)
		if !hasPermission{
			msg = err.Error()
			break
		}*/
		hasPermission, err := vc.appManagerService.CheckAppTenantPermission(reqAppWho)
		if !hasPermission {
			code = err_const.ERR_NO_PERMISSION
			msg = err.Error()
			break
		}

		//如果是租户端小程序，要检查用户是否是租户的成员，是否有权限进入租户
		isTenantApp := vc.appManagerService.IsTenantApp(*userwho)
		if isTenantApp {
			hasPermission := vc.tenantService.HasPermissionInTenant(userwho, reqAppWho.TenantId, reqAppWho.Uid)
			if hasPermission != true {
				code = err_const.ERR_NO_PERMISSION
				msg = "无权限"
				break
			}
		}

		tokenString, err := vc.miniAppUserService.UserLogin(*reqAppWho, uid)
		if err != nil {
			code = err_const.ERR_LOGIN_FAIL
			msg = err.Error()
			break
		}

		_, err = vc.tenantService.SwitchCurrentTenant(reqAppWho, reqAppWho.TenantId)
		if err != nil {
			code = err_const.ERR_LOGIN_FAIL
			msg = err.Error()
			break
		}
		resp := &Resp{}
		resp.AppToken = tokenString

		//data["token"] = tokenString
		response["data"] = resp
		break
	}

	response["code"] = code
	response["msg"] = msg
	ctxExt.JSON(200, response)
	return
}

func (vc *UserApiCtrl) UserCheckTokenMiddlewareHandler(ctx *fasthttp.RequestCtx) error {
	ctxExt := lego.RequestCtxExtent{ctx}
	//common.GetSessionData(&ctxExt)
	/*type Request struct {
		Token    string `json:"token,omitempty"`
		Timestamp int64 `json:"timestamp,omitempty"`
		TenantId    int64 `json:"tid,omitempty"`
		AppId    int64 `json:"appid,omitempty"`
	}*/

	//url querystring格式定义
	/*type UrlParam struct {
		Token    string `urlenc:"token"`
		Timestamp    string `urlenc:"timestamp"`
		LoginMode    int `urlenc:"loginmode"`

		AppToken string `urlenc:"apptoken"`
		AppTimestamp    string `urlenc:"apptimestamp"`
		//TenantId    int64 `urlenc:"tid"`
		//AppId    int64 `urlenc:"appid"`

	}*/

	type HeaderParam struct {
		Token     string
		Timestamp string
		LoginMode int

		AppToken     string
		AppTimestamp string
	}

	var headerparam = &HeaderParam{
		Token:     string(ctxExt.Request.Header.Peek("token")),
		Timestamp: string(ctxExt.Request.Header.Peek("timestamp")),
		LoginMode: utils.BytesToInt(ctxExt.Request.Header.Peek("loginmode")),

		AppToken:     string(ctxExt.Request.Header.Peek("apptoken")),
		AppTimestamp: string(ctxExt.Request.Header.Peek("apptimestamp")),
	}

	//var req Request
	/*var urlParam UrlParam
	if err := ctxExt.UrlDecode(&urlParam); err != nil {
		ctxExt.ResponseWithStateCode200(100, err.Error())
		return err
	}

	//优先使用url param
	if urlParam.Token == "" && headerparam != nil {
		urlParam.Token = headerparam.Token
		urlParam.Timestamp = headerparam.Timestamp
		urlParam.LoginMode = headerparam.LoginMode
		urlParam.AppToken = headerparam.AppToken
		urlParam.AppTimestamp = headerparam.AppTimestamp
	}*/
	// TODO 后续考虑还需要check sys token吗？
	tokenwho, _, err := vc.CheckToken(&ctxExt, headerparam.Token, headerparam.Timestamp, headerparam.LoginMode)
	if err != nil {
		if strings.HasPrefix(err.Error(), "token is expired") {
			ctxExt.ResponseWithStateCode200(err_const.ERR_TOKEN_EXPIRED, err.Error())
			return err
		}
		ctxExt.ResponseWithStateCode200(1, err.Error())
		return err
	}

	/*tokenWho := common.Who{
		TenantId:0, // TODO 这里要修改
		MiniAppId:user.User.Id,
		Uid: user.User.Id,

	}*/
	// TODO暂时先这样吧, 以下的接口不进行app权鉴
	var appTokenWho *common.Who
	reqpath := string(ctxExt.Path())
	//注意，这里sys/后面的斜杠不能去掉，遇到过一个问题，比如/api/sysOrder不是系统级的url，但是也会被/api/sys匹配到
	if !strings.HasPrefix(reqpath, "/api/sys/") { //属于系统级的接口，不需要apptoken,
		AppToken := headerparam.AppToken
		AppTimestamp := headerparam.AppTimestamp
		appTokenWho, err = vc.MiniAppCheckToken(&ctxExt, AppToken, AppTimestamp)
		if err != nil {
			ctxExt.ResponseWithStateCode200(1, err.Error())
			return err
		}

		hasPermission, err := vc.appManagerService.CheckAppTenantPermission(appTokenWho)
		if !hasPermission {
			ctxExt.ResponseWithStateCode200(1, err.Error())
			return err
		}
	}

	sessionData := &common.SesionData{
		SysTokenWho: tokenwho,
		AppTokenWho: appTokenWho,
	}

	common.SetSessionData(&ctxExt, sessionData)
	return nil
}

/* 获取token
* params:
  ---
*/
type WxUpdatePhoneRequest struct {
	EncryptedData string `json:"encryptedData,omitempty" vd:""`
	Iv string `json:"iv,omitempty" vd:""`
}

type WxUpdatePhoneResp struct {
}

func (vc *UserApiCtrl) WxUpdatePhone(ctx *fasthttp.RequestCtx) {
	ctxExt := lego.RequestCtxExtent{ctx}
	response := make(map[string]interface{})
	//data := make(map[string]interface{})


	code := 0
	msg := "success"

	type Response struct {
		PhoneNumber string `json:"phoneNumber"`
		//PurePhoneNumber string `json:"purePhoneNumber"`
		//CountryCode string `json:"countryCode"`
		//PurePhoneNumber string `json:"purePhoneNumber"`
		//CountryCode string `json:"countryCode"`
	}

	var resp Response
	for {

		sessionData := common.GetSessionData(&ctxExt)
		appwho := sessionData.AppTokenWho
		//rawcode := string(ctxExt.Peek("code"))
		//tid, _ := utils.StringToInt64(string(ctxExt.Peek("tid"))) //tenant id
		//appid, _ := utils.StringToInt64(string(ctxExt.Peek("appid"))) //tenant id
		//shareid := string(ctx.FormValue("shareid")) // 谁邀请的
		//rawData := string(ctx.PostArgs().Peek("rawData"))
		//BODY内json数据的定义



		var req WxUpdatePhoneRequest
		requestByte := ctxExt.PostBody()
		err := json.Unmarshal(requestByte, &req)
		if err != nil {
			code = 100
			msg = err.Error()
			break
		}


		if err = lego.Validate(req); err != nil {
			code = 100
			msg = err.Error()
			break
		}

		result, err := vc.wxUserservice.DecrptAndSavePhoneByWho(appwho, req.EncryptedData, req.Iv)
		if err != nil {
			code = 100
			msg = err.Error()
			break
		}

		resp.PhoneNumber = result.PhoneNumber
		//resp.PurePhoneNumber = result.PurePhoneNumber
		//resp.CountryCode = result.CountryCode
		response["data"] = resp
		break
	}

	response["code"] = code
	response["msg"] = msg
	ctxExt.JSON(200, response)
	return
}